/*
 * Copyright 2011-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.framework.concurrent.time;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

/**
 * 高并发场景下 {@link System#currentTimeMillis()} 的性能问题的优化
 *
 * @author liym
 * @since 2019-04-11 15:39 新建
 */
public final class SystemClock {

    private final long period;
    private final AtomicLong timeMillis;

    private SystemClock(long period) {
        this.period = period;
        this.timeMillis = new AtomicLong(System.currentTimeMillis());
        scheduleClockUpdating();
    }

    private static final class InstanceHolder {

        private static final SystemClock INSTANCE = new SystemClock(1);

    }

    private static SystemClock instance() {
        return InstanceHolder.INSTANCE;
    }

    private void scheduleClockUpdating() {
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setNameFormat("System-Clock-%d")
                .setDaemon(true)
                .build();

        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1, threadFactory);

        scheduler.scheduleAtFixedRate(
                () -> timeMillis.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);
    }

    private long currentTimeMillis() {
        return timeMillis.get();
    }

    public static long now() {
        return instance().currentTimeMillis();
    }

    public static String datetime() {
        Instant instant = Instant.ofEpochMilli(instance().currentTimeMillis());
        LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        return dateTime.toString();
    }

    public static void main(String[] args) {
        String datetime = datetime();
        System.out.println(datetime);
    }

}
